// Systemy kolejkowe 1.cpp : Defines the entry point for the console application.
//

#include "stdafx.h"
#include <queue>
#include <map>
#include <list>
#include <iostream>
#include <Windows.h>

#include "Network.h"
#include "Parameters.h"
#include "UniformParameters.h"
#include "SystemFacade.h"
#include "NetworkGraphics.h"

using namespace std;

//int _tmain(int argc, _TCHAR* argv[])
//{
//	string configuration = "D:/conf.k";
//
//	Network n;
//	n.loadFromFile(configuration);
//	com.output<< n.toString();
//
//	n.processTime(500);
//	n.printCommunicator();
//
//	com.output<< endl << n.toString();
//	
//	cin.get();
//	return 0;
//}

LPCTSTR ClsName = "SystemKolejkowy";
LPCTSTR WndName = "System kolejkowy";

#define TICK_INTERVAL	1

#define ID_RENDER_TIMER	1001
#define ID_START_BUTTON	(WM_USER + 1)
#define ID_STOP_BUTTON	(WM_USER + 2)
#define ID_LOAD_BUTTON	(WM_USER + 3)

SystemFacade *g_system = NULL;


void InitializeNetwork() 
{	
	g_system = new SystemFacade();
}

std::map<int, std::list<int>> PositionateNests()
{
	int nestsCount = g_system->getNestsCount();
	std::map<int, std::list<int>> position;	
	int consumedCount = 0;
	std::vector<bool> consumedIndex = std::vector<bool>(nestsCount);
	
	position[0] = std::list<int>();

	for (int i = 0; i < nestsCount; i++)
	{
		if(!g_system->hasNestInConnections(i))
		{
			position[i].push_back(i);
			consumedCount++;
			consumedIndex[i] = true;
		}
	}

	int currPosition = 0;

	while(consumedCount != nestsCount)
	{
		position[currPosition + 1] = std::list<int>();

		for(std::list<int>::iterator i = position[currPosition].begin(); i != position[currPosition].end(); i++)
		{
			std::vector<int> outputs = g_system->getNestOutConnections(*i);

			for(std::vector<int>::iterator j = outputs.begin(); j != outputs.end(); j++)
			{
				if(!consumedIndex[*j])
				{			
					position[currPosition + 1].push_back(*j);
					consumedCount++;
					consumedIndex[*j] = true;
				}
			}			
		}

		currPosition++;
	}

	return position;
}

std::map<int, RECT> DrawNests(NetworkGraphics &g, std::map<int, std::list<int>> nestsPositions) 
{
	std::map<int, RECT> drawnNests;
	int BLOCK_WIDTH = 300;
	int BLOCK_HEIGHT = 150;
	int x = 0, y = 0;

	for(std::map<int, std::list<int>>::iterator i = nestsPositions.begin(); i != nestsPositions.end(); i++)
	{
		y = 0;

		for(std::list<int>::iterator j = i->second.begin(); j != i->second.end(); j++)
		{
			bool machinesBusiness[1024];
			int machinesCount = g_system->getMachinesCount(*j);

			for(int k = 0; k < machinesCount; k++)
			{
				machinesBusiness[k] = !g_system->isMachineFree(*j, k);
			}

			drawnNests[*j] = g.drawNest(
				x,
				y,
				g_system->getBufferCapacity(*j),
				g_system->getBufferCount(*j),
				g_system->getMachinesCount(*j),
				machinesBusiness);
			
			y += BLOCK_HEIGHT;
		}		

		x += BLOCK_WIDTH;
	}

	return drawnNests;
}

void ConnectNests(NetworkGraphics &g, std::map<int, RECT> drawnNests) 
{
	for(std::map<int, RECT>::iterator i = drawnNests.begin(); i != drawnNests.end(); i++)
	{
		std::vector<int> output = g_system->getNestOutConnections(i->first);

		for(std::vector<int>::iterator j = output.begin(); j != output.end(); j++)
		{
			g.drawConnection(i->second, drawnNests[*j]);
		}
	}
}

POINTS lastPoint;

int dx = 0, dy = 0;
double inflateFactor = 0;

HWND hWndLog;
HWND hWndConfig;

void RepaintWindow(HWND hWnd) 
{
	RECT window;
	GetClientRect(hWnd, &window);

	HDC hDC = GetDC(hWnd);
	HDC hdcMem=CreateCompatibleDC(hDC);
	HBITMAP bitmap = CreateCompatibleBitmap(hdcMem, window.right, window.bottom);
	SelectObject(hdcMem, bitmap);

	NetworkGraphics g = NetworkGraphics(hdcMem);				
	FillRect(hdcMem, &window, (HBRUSH)GetStockObject(WHITE_BRUSH));

	g.translate(dx, dy);
	g.scale(inflateFactor);

	std::map<int, std::list<int>> nestsPositions = PositionateNests();
	std::map<int, RECT> drawnNests = DrawNests(g, nestsPositions);
	ConnectNests(g, drawnNests);
	
	BitBlt(hDC,440,0,window.right,window.bottom,hdcMem,0,0,SRCCOPY);
	DeleteObject(bitmap);
	DeleteDC(hdcMem);
	ReleaseDC(hWnd, hDC);				
}

std::string ReadWindowText(HWND hWnd)
{
	char buff[65536];
	SendMessage( hWnd, WM_GETTEXT, sizeof(buff), (LPARAM)buff );

	return std::string(buff);
}

LRESULT CALLBACK WndProcedure(HWND hWnd, UINT Msg,
	WPARAM wParam, LPARAM lParam)
{	


	switch(Msg)
	{			
	case WM_RBUTTONDOWN:
	case WM_LBUTTONDOWN:
		lastPoint.x = lastPoint.y = 0;
		break;	
	case WM_MOUSEMOVE:
		if(wParam & MK_LBUTTON || wParam & MK_RBUTTON)
		{			
			POINTS p = MAKEPOINTS(lParam);

			if(lastPoint.x == 0 && lastPoint.y == 0)
			{
				lastPoint = p;
			}

			if(wParam & MK_LBUTTON)
			{	
				dx += (p.x - lastPoint.x);
				dy += (p.y - lastPoint.y);
			}

			if(wParam & MK_RBUTTON)
			{
				inflateFactor += 
					(sqrt((double)p.x * p.x + p.y * p.y) > sqrt((double)lastPoint.x * lastPoint.x + lastPoint.y * lastPoint.y) ? 1 : -1) *
					sqrt((double)(p.x - lastPoint.x) * (p.x - lastPoint.x) + (p.y - lastPoint.y) * (p.y - lastPoint.y)) / 200.0;
			}

			lastPoint = p;

			RepaintWindow(hWnd);
		}	
		break;
	case WM_COMMAND:

		if(wParam == ID_START_BUTTON)
		{
			SetTimer(hWnd, ID_RENDER_TIMER, TICK_INTERVAL, NULL);
		}

		if(wParam == ID_STOP_BUTTON)
		{
			KillTimer(hWnd, ID_RENDER_TIMER);
		}

		if(wParam == ID_LOAD_BUTTON)
		{
			g_system->loadNetwork(ReadWindowText(hWndConfig));
		}
		
		break;
	case WM_TIMER:
		g_system->tick();
		RepaintWindow(hWnd);

		SetWindowText(hWndLog, g_system->readLog().c_str());

		break;
	case WM_DESTROY:
		PostQuitMessage(WM_QUIT);
		break;
	default:	
		return DefWindowProc(hWnd, Msg, wParam, lParam);
	}

	return 0;
}

INT WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
	LPSTR lpCmdLine, int nCmdShow)
{
	MSG        Msg;
	HWND       hWnd;
	WNDCLASSEX WndClsEx;
	
	InitializeNetwork();


	WndClsEx.cbSize        = sizeof(WNDCLASSEX);
	WndClsEx.style         = CS_HREDRAW | CS_VREDRAW;
	WndClsEx.lpfnWndProc   = WndProcedure;
	WndClsEx.cbClsExtra    = 0;
	WndClsEx.cbWndExtra    = 0;
	WndClsEx.hIcon         = LoadIcon(NULL, IDI_APPLICATION);
	WndClsEx.hCursor       = LoadCursor(NULL, IDC_ARROW);
	WndClsEx.hbrBackground = (HBRUSH)GetStockObject(WHITE_BRUSH);
	WndClsEx.lpszMenuName  = NULL;
	WndClsEx.lpszClassName = ClsName;
	WndClsEx.hInstance     = hInstance;
	WndClsEx.hIconSm       = LoadIcon(NULL, IDI_APPLICATION);
	
	RegisterClassEx(&WndClsEx);
	
	hWnd = CreateWindow(ClsName,
		WndName,
		WS_OVERLAPPEDWINDOW,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		CW_USEDEFAULT,
		NULL,
		NULL,
		hInstance,
		NULL);
	
	//CreateWindow("Static", "LOG:", WS_CHILD | WS_VISIBLE, 0, 0, 200, 20, hWnd, NULL, hInstance, NULL);
	//CreateWindow("Static", "CONFIG:",	WS_CHILD | WS_VISIBLE, 210, 0, 200, 20, hWnd, NULL, hInstance, NULL);

	hWndLog = CreateWindow("Edit", "", WS_BORDER | WS_CHILD  | ES_MULTILINE, 0, 20, 200, 600, hWnd, NULL, hInstance, NULL);	
	hWndConfig = CreateWindow("Edit", "", WS_BORDER | WS_CHILD  | ES_MULTILINE, 210, 20, 200, 600, hWnd, NULL, hInstance, NULL);	

	CreateWindow("Button", "Start", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, 0, 630, 100, 25, hWnd, (HMENU)ID_START_BUTTON, hInstance, NULL);
	CreateWindow("Button", "Stop", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, 110, 630, 100, 25, hWnd, (HMENU)ID_STOP_BUTTON, hInstance, NULL);
	CreateWindow("Button", "Load", BS_PUSHBUTTON | WS_CHILD | WS_VISIBLE, 220, 630, 100, 25, hWnd, (HMENU)ID_LOAD_BUTTON, hInstance, NULL);


	DWORD err = GetLastError();

	if( !hWnd )
		return 0;
	
	ShowWindow(hWnd, SW_SHOWNORMAL);
	UpdateWindow(hWnd);		

	while( GetMessage(&Msg, NULL, 0, 0) )
	{
		TranslateMessage(&Msg);
		DispatchMessage(&Msg);
	}

	return Msg.wParam;
}
